package com.example.demo.util;


import com.example.demo.signature.PublicUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.springframework.util.CollectionUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.security.cert.X509Certificate;
import java.util.*;


@Slf4j
public class HttpClientUtils {
    private static HttpClient httpclient = null;
    private static HttpContext context = new BasicHttpContext();
    private static RequestConfig requestConfig;//不限请求超时
    private static RequestConfig requestConfigWithTimeout;//限制超时时间
    private static RequestConfig requestConfigWithTimeout30;//限制超时时间30秒

    /**
     * http调用错误结果(false)
     */
    public static final String HTTP_RESPONSE_ERROR = "false";

    /**
     * 校验http返回结果
     *
     * @param httpResponse http工具返回的字符串结果
     * @return 返回不为空且返回不为"false"时true;
     */
    public static boolean isHttpResponseOk(String httpResponse) {
        return !PublicUtils.isEmptyString(httpResponse) && !HTTP_RESPONSE_ERROR.equalsIgnoreCase(httpResponse);
    }

    static {
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(800);
        cm.setDefaultMaxPerRoute(100);
        /*HttpHost localhost=new HttpHost("localhost", 8080);
        cm.setMaxPerRoute(new HttpRoute(localhost), 50);*/
        httpclient = HttpClients.custom()
                .setConnectionManager(cm)
                .build();

        requestConfig = RequestConfig.custom()
                .setConnectTimeout(3000)//连接超时
                .setConnectionRequestTimeout(5000)//从池中获取连接超时
                //.setSocketTimeout(5000)//响应超时
                .build();

        requestConfigWithTimeout = RequestConfig.custom()
                .setConnectTimeout(3000)//连接超时
                .setConnectionRequestTimeout(5000)//从池中获取连接超时
                .setSocketTimeout(5000)//响应超时
                .build();

        requestConfigWithTimeout30 = RequestConfig.custom()
                .setConnectTimeout(3000)//连接超时
                .setConnectionRequestTimeout(5000)//从池中获取连接超时
                .setSocketTimeout(30000)//响应超时
                .build();
    }

    /**
     * 表单提交（无访问超时）
     *
     * @param url    url
     * @param params form参数
     * @return 结果
     */
    public static String doPost(String url, Map<String, String> params) {
        return doPost(url, params, false);
    }

    /**
     * 表单提交（无访问超时）
     *
     * @param url     url
     * @param params  form参数
     * @param headers header头
     * @return 结果
     */
    public static String doPost(String url, Map<String, String> params, Map<String, Object> headers) {
        return doPost(url, params, headers, false);
    }

    /**
     * 表单提交
     *
     * @param url        url
     * @param params     form参数
     * @param hasTimeout 是否超时
     * @return 结果
     */
    public static String doPost(String url, Map<String, String> params, boolean hasTimeout) {
        return doPost(url, params, null, hasTimeout);
    }

    // 初始化一个跳过 SSL 验证的 HttpClient
    private static CloseableHttpClient createHttpClientWithNoSslVerification() throws Exception {
        // 创建一个信任所有证书的 TrustManager
        TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(X509Certificate[] chain, String authType) {
                        // 不检查客户端证书
                    }

                    @Override
                    public void checkServerTrusted(X509Certificate[] chain, String authType) {
                        // 不检查服务器证书
                    }

                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        return new X509Certificate[0];
                    }
                }
        };

        // 创建 SSL 上下文并初始化为信任所有证书
        SSLContext sslContext = SSLContextBuilder.create()
                .loadTrustMaterial((chain, authType) -> true)
                .build();
        sslContext.init(null, trustAllCerts, new java.security.SecureRandom());

        // 创建 HttpClient 并设置 SSLContext 和 HostnameVerifier
        return HttpClients.custom()
                .setSSLContext(sslContext)
                .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)  // 跳过主机名验证
                .build();
    }

    /**
     * 表单提交
     *
     * @param url        url
     * @param params     form参数
     * @param headers    header头
     * @param hasTimeout 是否超时
     * @return 结果
     */
    public static String doPost(String url, Map<String, String> params, Map<String, Object> headers, boolean hasTimeout) {
        HttpPost httpPost = new HttpPost(url);
        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        if (params != null && !params.isEmpty()) {
            for (String key : params.keySet()) {
                nvps.add(new BasicNameValuePair(key, Objects.toString(params.get(key), "")));
            }
        }
        try {
            httpPost.setEntity(new UrlEncodedFormEntity(nvps, "utf-8"));
            if (headers != null && !headers.isEmpty()) {
                for (Map.Entry<String, Object> entry : headers.entrySet()) {
                    httpPost.addHeader(entry.getKey(), Objects.toString(entry.getValue(), ""));
                }
            }
            if (hasTimeout) {
                httpPost.setConfig(requestConfigWithTimeout);
            } else {
                httpPost.setConfig(requestConfig);
            }

            // 使用定制的 HttpClient 进行请求
            CloseableHttpClient httpclient = createHttpClientWithNoSslVerification();
            HttpResponse response = httpclient.execute(httpPost, context);

            return EntityUtils.toString(response.getEntity(), "UTF-8");
        } catch (ClientProtocolException e) {
            //HttpClientUtils.log.error(e.getMessage() + ";" + url + ";" + JsonUtil.toJson(params));
            return "false";
        } catch (Exception e) {
            //HttpClientUtils.log.error(e.getMessage() + ";" + url + ";" + JsonUtil.toJson(params));
            return "false";
        }
    }

    /**
     * GET请求
     *
     * @param url           请求url
     * @param urlQueryParam url链接中的参数
     * @param header        header
     * @return 请求结果
     */
    public static String doGetWithTimeout(String url, Map<String, Object> urlQueryParam, Map<String, Object> header) {
        return doRequestWithHeader(new HttpGet(), url, urlQueryParam, header, true);
    }

    /**
     * 基础请求访问
     *
     * @param httpRequest   http请求方法
     * @param url           请求url
     * @param urlQueryParam url链接中的参数
     * @param header        请求头
     * @return 请求结果
     */
    private static String doRequestWithHeader(HttpRequestBase httpRequest, String url, Map<String, Object> urlQueryParam, Map<String, Object> header, boolean hasTimeout) {
        try {
            String fullUrlWithQuery = createFullUrlWithQuery(url, urlQueryParam);
            if (!CollectionUtils.isEmpty(header)) {
                for (Map.Entry<String, Object> entry : header.entrySet()) {
                    httpRequest.addHeader(entry.getKey(), Objects.toString(entry.getValue(), ""));
                }
            }
            httpRequest.setURI(URI.create(fullUrlWithQuery));
            if (hasTimeout) {
                httpRequest.setConfig(requestConfigWithTimeout);
            } else {
                httpRequest.setConfig(requestConfig);
            }
            HttpResponse response = httpclient.execute(httpRequest, context);
            int state = response.getStatusLine().getStatusCode();
            if (state >= 200 && state < 300) {
                return EntityUtils.toString(response.getEntity(), "UTF-8");
            } else {
                log.error("远程接口调用错误： URL=" + fullUrlWithQuery + "响应：" + EntityUtils.toString(response.getEntity(), "UTF-8"));
                return "false";
            }
        } catch (Exception e) {
            HttpClientUtils.log.error(e.getMessage());
            return "false";
        }
    }

    /**
     * 创建包含参数的URL
     *
     * @param url           请求url
     * @param urlQueryParam url链接中的参数
     * @return 含参数的URL
     */
    public static String createFullUrlWithQuery(String url, Map<String, Object> urlQueryParam) {
        StringBuilder fullUrlWithQuery = new StringBuilder(url.contains("?") ? url + "&" : url + "?");
        StringJoiner urlQuery = new StringJoiner("&");
        if (!CollectionUtils.isEmpty(urlQueryParam)) {
            for (Map.Entry<String, Object> entry : urlQueryParam.entrySet()) {
                try {
                    urlQuery.add(entry.getKey() + "=" + URLEncoder.encode(Objects.toString(entry.getValue(), ""), "utf-8"));
                } catch (UnsupportedEncodingException e) {
                    //none
                }
            }
        }
        return fullUrlWithQuery.append(urlQuery).toString();
    }

    public static String postTxt(String url, String txt) {
        return postTxt(url, txt, "application/json;charset=UTF-8", false);
    }

    public static String postJson(String url, String json) {
        return postTxt(url, json, "application/json;charset=UTF-8", false);
    }

    public static String postJson(String url, String json, boolean timeOut) {
        return postTxt(url, json, "application/json;charset=UTF-8", timeOut);
    }

    public static String postJson(String url, String json, Map<String, String> headers) {
        HttpPost httpPost = new HttpPost(url);

        try {
            StringEntity entity = new StringEntity(json, "UTF-8");
            entity.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, "application/json;charset=UTF-8"));
            if (null != headers) {
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    httpPost.addHeader(entry.getKey(), entry.getValue());
                }
            }
            httpPost.setEntity(entity);
            httpPost.setConfig(requestConfig);
            HttpResponse response = httpclient.execute(httpPost, context);

            return EntityUtils.toString(response.getEntity(), "UTF-8");
        } catch (ClientProtocolException e) {
            HttpClientUtils.log.error(e.getMessage());
            return "false";
        } catch (Exception e) {
            HttpClientUtils.log.error(e.getMessage());
            return "false";
        }
    }

    public static String putJson(String url, String json, Map<String, String> headers) {
        HttpPut httpPut = new HttpPut(url);

        try {
            StringEntity entity = new StringEntity(json, "UTF-8");
            entity.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, "application/json;charset=UTF-8"));
            if (null != headers) {
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    httpPut.addHeader(entry.getKey(), entry.getValue());
                }
            }
            httpPut.setEntity(entity);
            httpPut.setConfig(requestConfig);
            HttpResponse response = httpclient.execute(httpPut, context);

            return EntityUtils.toString(response.getEntity(), "UTF-8");
        } catch (ClientProtocolException e) {
            HttpClientUtils.log.error(e.getMessage());
            return "false";
        } catch (Exception e) {
            HttpClientUtils.log.error(e.getMessage());
            return "false";
        }
    }

    public static String doDel(String url, Map<String, String> headers) {
        HttpDelete httpDelete = new HttpDelete(url);

        try {
            if (null != headers) {
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    httpDelete.addHeader(entry.getKey(), entry.getValue());
                }
            }
            httpDelete.setConfig(requestConfig);
            HttpResponse response = httpclient.execute(httpDelete, context);

            return EntityUtils.toString(response.getEntity(), "UTF-8");
        } catch (ClientProtocolException e) {
            HttpClientUtils.log.error(e.getMessage());
            return "false";
        } catch (Exception e) {
            HttpClientUtils.log.error(e.getMessage());
            return "false";
        }
    }

    public static String postTxt(String url, String txt, String contentType, boolean hasTimeout) {
        HttpPost httpPost = new HttpPost(url);
        try {
            StringEntity entity = new StringEntity(txt, "UTF-8");
            entity.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, contentType));
            httpPost.setEntity(entity);
            if (hasTimeout) {
                httpPost.setConfig(requestConfigWithTimeout);
            } else {
                httpPost.setConfig(requestConfig);
            }
            HttpResponse response = httpclient.execute(httpPost, context);

            return EntityUtils.toString(response.getEntity(), "UTF-8");
        } catch (ClientProtocolException e) {
            HttpClientUtils.log.error(e.getMessage() + ";" + url + ";" + txt, e);
            return "false";
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            HttpClientUtils.log.error(e.getMessage() + ";" + url + ";" + txt);
            return "false";
        }
    }

    /**
     * 特定30秒超时的http 调用
     *
     * @param url 地址
     * @param txt json
     * @return 调用结果
     */
    public static String postJsonForTimeout30(String url, String txt) {
        HttpPost httpPost = new HttpPost(url);
        try {
            StringEntity entity = new StringEntity(txt, "UTF-8");
            entity.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, "application/json;charset=UTF-8"));
            httpPost.setEntity(entity);
            httpPost.setConfig(requestConfigWithTimeout30);
            HttpResponse response = httpclient.execute(httpPost, context);

            return EntityUtils.toString(response.getEntity(), "UTF-8");
        } catch (ClientProtocolException e) {
            HttpClientUtils.log.error(e.getMessage() + ";" + url + ";" + txt, e);
            return "false";
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            HttpClientUtils.log.error(e.getMessage() + ";" + url + ";" + txt);
            return "false";
        }
    }

    /**
     * 特定5秒超时的http 调用
     *
     * @param url 地址
     * @param txt json
     * @return 调用结果
     */
    public static String postJsonForTimeout5(String url, String txt) {
        HttpPost httpPost = new HttpPost(url);
        try {
            StringEntity entity = new StringEntity(txt, "UTF-8");
            entity.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, "application/json;charset=UTF-8"));
            httpPost.setEntity(entity);
            httpPost.setConfig(requestConfigWithTimeout);
            HttpResponse response = httpclient.execute(httpPost, context);
            return EntityUtils.toString(response.getEntity(), "UTF-8");
        } catch (ClientProtocolException e) {
            HttpClientUtils.log.error(e.getMessage() + ";" + url + ";" + txt, e);
            return "false";
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            HttpClientUtils.log.error(e.getMessage() + ";" + url + ";" + txt);
            return "false";
        }
    }

    /**
     * POST提交请求,获取Stream流结果
     *
     * @param url         请求URL
     * @param txt         请求内容
     * @param contentType 内容类型
     * @param hasTimeout  是否超时
     * @return Stream流
     */
    public static InputStream postTxtForStream(String url, String txt, String contentType, boolean hasTimeout) {
        HttpPost httpPost = new HttpPost(url);
        try {
            StringEntity entity = new StringEntity(txt, "UTF-8");
            entity.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, contentType));
            httpPost.setEntity(entity);
            if (hasTimeout) {
                httpPost.setConfig(requestConfigWithTimeout);
            } else {
                httpPost.setConfig(requestConfig);
            }
            HttpResponse response = httpclient.execute(httpPost, context);
            return response.getEntity().getContent();
        } catch (ClientProtocolException e) {
            HttpClientUtils.log.error(e.getMessage() + ";" + url + ";" + txt, e);
            return null;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            HttpClientUtils.log.error(e.getMessage() + ";" + url + ";" + txt);
            return null;
        }
    }

    /**
     * POST提交json请求,获取Stream流结果
     *
     * @param url  请求URL
     * @param json 请求内容
     * @return Stream流
     */
    public static InputStream postJsonForStream(String url, String json) {
        return postTxtForStream(url, json, "application/json;charset=UTF-8", false);
    }

    /**
     * GET提交请求,获取Stream流结果
     *
     * @param url 请求URL
     * @return Stream流
     */
    public static InputStream doGetForStream(String url) {
        HttpGet httpGet = new HttpGet(url);
        try {
            HttpResponse response = httpclient.execute(httpGet, context);

            int state = response.getStatusLine().getStatusCode();
            if (state >= 200 && state < 300) {
                return response.getEntity().getContent();
            } else {
                log.error("远程接口调用错误： URL=" + url + "响应：" + EntityUtils.toString(response.getEntity(), "UTF-8"));
                return null;
            }
        } catch (ClientProtocolException e) {
            HttpClientUtils.log.error(e.getMessage());
            return null;
        } catch (Exception e) {
            HttpClientUtils.log.error(e.getMessage());
            return null;
        }
    }

    public static String doGet(String url, Map<String, String> headers) {
        HttpGet httpGet = new HttpGet(url);
        try {
            if (null != headers) {
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    httpGet.addHeader(entry.getKey(), entry.getValue());
                }
            }
            HttpResponse response = httpclient.execute(httpGet, context);

            //TODO 请吴昌对平台进行判断，此处状态非200系列当作异常处理会不会有问题
            // 请求返回200时才当作正常返回处理
            int state = response.getStatusLine().getStatusCode();
            if (state >= 200 && state < 300) {
                return EntityUtils.toString(response.getEntity(), "UTF-8");
            } else {
                log.error("远程接口调用错误： URL=" + url + "响应：" + EntityUtils.toString(response.getEntity(), "UTF-8"));
                return "false";
            }
        } catch (ClientProtocolException e) {
            HttpClientUtils.log.error(e.getMessage());
            return "false";
        } catch (Exception e) {
            HttpClientUtils.log.error(e.getMessage());
            return "false";
        } finally {
            httpGet.releaseConnection();
        }
    }

    public static String doGet(String url) {
        HttpGet httpGet = new HttpGet(url);
        try {
            // 使用定制的 HttpClient 进行请求
            CloseableHttpClient httpclient = createHttpClientWithNoSslVerification();
            HttpResponse response = httpclient.execute(httpGet, context);

            //TODO 请吴昌对平台进行判断，此处状态非200系列当作异常处理会不会有问题
            // 请求返回200时才当作正常返回处理
            int state = response.getStatusLine().getStatusCode();
            if (state >= 200 && state < 300) {
                return EntityUtils.toString(response.getEntity(), "UTF-8");
            } else {
                log.error("远程接口调用错误： URL=" + url + "响应：" + EntityUtils.toString(response.getEntity(), "UTF-8"));
                return "false";
            }
        } catch (ClientProtocolException e) {
            HttpClientUtils.log.error(e.getMessage());
            return "false";
        } catch (Exception e) {
            HttpClientUtils.log.error(e.getMessage());
            return "false";
        } finally {
            httpGet.releaseConnection();
        }
    }

    public static String doGetYuQing(String url) {
        HttpGet httpGet = new HttpGet(url);
        try {
            httpGet.setConfig(requestConfig);
            HttpResponse response = httpclient.execute(httpGet, context);

            //TODO 请吴昌对平台进行判断，此处状态非200系列当作异常处理会不会有问题
            // 请求返回200时才当作正常返回处理
            int state = response.getStatusLine().getStatusCode();
            if (state >= 200 && state < 300) {
                return EntityUtils.toString(response.getEntity(), "UTF-8");
            } else {
                log.error("远程接口调用错误： URL=" + url + "响应：" + EntityUtils.toString(response.getEntity(), "UTF-8"));
                return "false";
            }
        } catch (ClientProtocolException e) {
            HttpClientUtils.log.error(e.getMessage());
            return "false";
        } catch (Exception e) {
            HttpClientUtils.log.error(e.getMessage());
            return "false";
        } finally {
            httpGet.releaseConnection();
        }
    }

    public static String doGetWithTimeout(String url) {
        HttpGet httpGet = new HttpGet(url);
        try {
            httpGet.setConfig(requestConfigWithTimeout);
            HttpResponse response = httpclient.execute(httpGet, context);

            //TODO 请吴昌对平台进行判断，此处状态非200系列当作异常处理会不会有问题
            // 请求返回200时才当作正常返回处理
            int state = response.getStatusLine().getStatusCode();
            if (state >= 200 && state < 300) {
                return EntityUtils.toString(response.getEntity(), "UTF-8");
            }
        } catch (ClientProtocolException e) {
            HttpClientUtils.log.error(e.getMessage());
            return "false";
        } catch (Exception e) {
            HttpClientUtils.log.error(e.getMessage());
            return "false";
        }
        return "false";
    }


    public static void main(String[] args) throws Exception {
//        System.out.print(HttpClientUtils.doGet("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx3d8211bcc8e896de&secret=271899d243cc1e776db2847f387af99b"));
        /*String xml = "<xml><ToUserName><![CDATA[gh_5109c35bb611]]></ToUserName><FromUserName><![CDATA[oq6vUt3tXOJdGfOqGabq3nn3rlOA]]></FromUserName><CreateTime>1388631016</CreateTime><MsgType><![CDATA[event]]></MsgType><Event><![CDATA[CLICK]]></Event><EventKey><![CDATA[wedding_wall]]></EventKey></xml>";
        String xml1 = "<xml><ToUserName><![CDATA[gh_5109c35bb611]]></ToUserName><FromUserName><![CDATA[oq6vUt3tXOJdGfOqGabq3nn3rlOA]]></FromUserName><CreateTime>1388631029</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[@张三]]></Content><MsgId>5964124855775840963</MsgId></xml>";
        postTxt("http://127.0.0.1:8081/market/weixin?signature=1&nonce=1&timestamp=1", xml1, "text/xml");*/

        /*ExecutorService executorService = Executors.newFixedThreadPool(50);
        for(int i=0;i<50;i++)
        {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    String result = postJson("http://localhost:8080/cloud/xinhua/api/startLive", "{}");
                    //if(!"false".equals(result))
                        System.out.println(result);
                    //System.out.println(System.currentTimeMillis());
                }
            });
        }*/
//        String val = postJson("https://sz.qukanvideo.com/cloud/services/client/auth/loginByApp", "{\"userName\":\"qk\",\"password\":\"1\",\"clientInfo\":{}}");
        /*Map<String, Object> param = new HashMap<String, Object>();
        long n = System.currentTimeMillis();
        param.put("nonce", n);
        param.put("appKey", "thd02kswhswyi84h");
        param.put("signature", AuthUtil.getSignature("appKey=thd02kswhswyi84h&nonce=" +n, "tMJjrWc8w1oi8aNO"));
        String val = postJson("http://localhost/cloud/open/sync/query", JsonUtil.toJson(param));*/

    }

}
